LoRaWAN设备与物联网平台的通信数据格式为透传/自定义,因此需要使用消息解析脚本,解析上下行数据。本文以LoRaWAN温湿度传感器为例,介绍LoRaWAN设备消息解析脚本的编辑和调试方法。
步骤一:编辑脚本
- 在物联网平台控制台,创建连网方式为LoRaWAN的产品。
- 为该产品定义物模型。功能定义具体方法,请参见单个添加物模型。
本示例中,定义了以下属性、事件和服务:
表 1. 属性 标识符 数据类型 取值范围 读写类型 Temperature int32 -40~55 读写 Humidity int32 1~100 读写 表 2. 服务 标识符 调用方式 输入参数 SetTempHumiThreshold 异步 四个输入参数,数据类型均为int32: - 温度过高告警阈值(标识符:MaxTemp)
- 温度过低告警阈值(标识符:MinTemp)
- 湿度过高告警阈值(标识符:MaxHumi)
- 湿度过低告警阈值(标识符:MinHumi)
表 3. 事件 标识符 事件类型 输入参数 TempError 告警 温度Temperature HumiError 告警 湿度Humidity - 编写脚本。
在物联网平台控制台,产品详情页面的消息解析页签下,选择脚本语言,编写脚本。
支持的脚本语言 设备自定义数据格式转Alink JSON格式数据的函数(上行通信) Alink JSON格式数据转为设备自定义数据格式的函数(下行通信) JavaScript(ECMAScript 5) rawDataToProtocol protocolToRawData Python 2.7 raw_data_to_protocol protocol_to_raw_data PHP 7.2 rawDataToProtocol protocolToRawData 本文示例的语言为JavaScript(ECMAScript 5)。
脚本中,解析下行数据的函数protocolToRawData中,需设定输出结果的起始三个字节,用于指定下行端口号和下行消息类型。否则,系统会丢掉下行帧。 设备实际接收的数据中不会包含这三个字节。
表 4. 下行数据解析输出结果起始三字节 LoRa Downlink 字节数 说明 DFlag 1 固定为0x5D。 FPort 1 下行端口号。 DHDR 1 可选: - 0:表示 “Unconfirmed Data Down”数据帧。
- 1:表示 “Confirmed Data Down”数据帧。
例如,
0x5D 0x0A 0x00
表示下行帧端口号为10,数据帧为“Unconfirmed Data Down”。完整的示例脚本Demo,请参见本文附录:示例脚本。
步骤二: 在线测试脚本
在数据解析编辑器中,使用模拟数据测试脚本。
- 设备上报数据模拟解析 。
在模拟输入框中,选择模拟类型为设备上报数据,输入模拟数据000102,单击运行。
运行结果为:
{ "method": "thing.event.property.post", "id": "12345", "params": { "Temperature": 1, "Humidity": 2 }, "version": "1.1" }
- 设备接收数据模拟解析 。
选择模拟类型为设备接收数据,输入以下JSON格式的下行模拟数据,单击运行。
{ "method": "thing.service.SetTempHumiThreshold", "id": "12345", "version": "1.1", "params": { "MaxTemp": 50, "MinTemp": 8, "MaxHumi": 90, "MinHumi": 10 } }
运行结果为:
0x5d0a000332085a0a
步骤三:提交脚本
确认脚本可以正确解析数据后,单击提交,将该脚本提交到物联网平台系统,以供数据上下行时,物联网平台调用该脚本解析数据。
说明 仅提交后的脚本才能被物联网平台调用;草稿状态的脚本不能被调用。
步骤四:使用真实设备调试
脚本提交后,正式使用之前,请使用真实设备进行测试。LoRaWAN节点设备如何发送和接收数据,请参见模组厂商的相关手册。
- 测试LoRaWAN设备上报温湿度属性。
- 使用设备端发送数据,例如000102。
- 在该设备的设备详情页 页签下,查看设备上报的属性数据。
- 测试LoRaWAN设备上报事件。
- 使用设备端发送事件数据,如,温度告警数据0102,或湿度告警数据0202。
- 在该设备的设备详情页 页签下,查看设备上报的事件数据。
- 测试调用LoRaWAN设备服务。
- 在物联网平台控制台,选择 。
- 选择要调试的产品和设备,并选择调试真实设备,功能选择为温度湿度阈值(SetTempHumiThreshold),输入以下数据后,单击发送指令。
{ "MaxTemp": 50, "MinTemp": 8, "MaxHumi": 90, "MinHumi": 10 }
- 检查设备端是否接收到服务调用命令。
- 在该设备的设备详情页 页签下,查看设备服务调用数据。
附录:示例脚本
根据以上产品和其功能定义,编辑的示例脚本如下:
var ALINK_ID = "12345";
var ALINK_VERSION = "1.1";
var ALINK_PROP_POST_METHOD = 'thing.event.property.post';
var ALINK_EVENT_TEMPERR_METHOD = 'thing.event.TempError.post';
var ALINK_EVENT_HUMIERR_METHOD = 'thing.event.HumiError.post';
var ALINK_PROP_SET_METHOD = 'thing.service.property.set';
var ALINK_SERVICE_THSET_METHOD = 'thing.service.SetTempHumiThreshold';
/*
* 示例数据:
* 传入参数:
* 000102 // 共3个字节
* 输出结果:
* {"method":"thing.event.property.post", "id":"12345", "params":{"Temperature":1,"Humidity":2}, "version":"1.1"}
* 传入参数:
* 0102 // 共2个字节
* 输出结果:
* {"method":"thing.event.TempError.post","id":"12345","params":{"Temperature":2},"version":"1.1"}
* 传入参数:
* 0202 // 共2个字节
* 输出结果:
* {"method":"thing.event.HumiError.post","id":"12345","params":{"Humidity":2},"version":"1.1"}
*/
function rawDataToProtocol(bytes)
{
var uint8Array = new Uint8Array(bytes.length);
for (var i = 0; i < bytes.length; i++)
{
uint8Array[i] = bytes[i] & 0xff;
}
var params = {};
var jsonMap = {};
var dataView = new DataView(uint8Array.buffer, 0);
var cmd = uint8Array[0]; // command
if (cmd === 0x00)
{
params['Temperature'] = dataView.getInt8(1);
params['Humidity'] = dataView.getInt8(2);
jsonMap['method'] = ALINK_PROP_POST_METHOD;
}
else if (cmd == 0x01)
{
params['Temperature'] = dataView.getInt8(1);
jsonMap['method'] = ALINK_EVENT_TEMPERR_METHOD;
}
else if (cmd == 0x02)
{
params['Humidity'] = dataView.getInt8(1);
jsonMap['method'] = ALINK_EVENT_HUMIERR_METHOD;
}
else
{
return null;
}
jsonMap['version'] = ALINK_VERSION;
jsonMap['id'] = ALINK_ID;
jsonMap['params'] = params;
return jsonMap;
}
/*
* 示例数据:
* 传入参数:
* {"method":"thing.service.SetTempHumiThreshold", "id":"12345", "version":"1.1", "params":{"MaxTemp":50, "MinTemp":8, "MaxHumi":90, "MinHumi":10}}
* 输出结果:
* 0x5d0a000332085a0a
*/
function protocolToRawData(json)
{
var id = json['id'];
var method = json['method'];
var version = json['version'];
var payloadArray = [];
// 追加下行帧头部。
payloadArray = payloadArray.concat(0x5d);
payloadArray = payloadArray.concat(0x0a);
payloadArray = payloadArray.concat(0x00);
if (method == ALINK_SERVICE_THSET_METHOD)
{
var params = json['params'];
var maxtemp = params['MaxTemp'];
var mintemp = params['MinTemp'];
var maxhumi = params['MaxHumi'];
var minhumi = params['MinHumi'];
payloadArray = payloadArray.concat(0x03);
if (maxtemp !== null)
{
payloadArray = payloadArray.concat(maxtemp);
}
if (mintemp !== null)
{
payloadArray = payloadArray.concat(mintemp);
}
if (maxhumi !== null)
{
payloadArray = payloadArray.concat(maxhumi);
}
if (minhumi !== null)
{
payloadArray = payloadArray.concat(minhumi);
}
}
return payloadArray;
}
/**
* 将设备自定义Topic数据转换为JSON格式数据,设备上报数据到物联网平台时调用。
* 入参:topic,字符串,设备上报消息的Topic。
* 入参:rawData,byte[]数组,不能为空。
* 出参:jsonObj,对象,不能为空。
*/
function transformPayload(topic, rawData) {
var jsonObj = {}
return jsonObj;
}
// 以下是部分辅助函数。
function buffer_uint8(value)
{
var uint8Array = new Uint8Array(1);
var dv = new DataView(uint8Array.buffer, 0);
dv.setUint8(0, value);
return [].slice.call(uint8Array);
}
function buffer_int16(value)
{
var uint8Array = new Uint8Array(2);
var dv = new DataView(uint8Array.buffer, 0);
dv.setInt16(0, value);
return [].slice.call(uint8Array);
}
function buffer_int32(value)
{
var uint8Array = new Uint8Array(4);
var dv = new DataView(uint8Array.buffer, 0);
dv.setInt32(0, value);
return [].slice.call(uint8Array);
}
function buffer_float32(value)
{
var uint8Array = new Uint8Array(4);
var dv = new DataView(uint8Array.buffer, 0);
dv.setFloat32(0, value);
return [].slice.call(uint8Array);
}